package com.flying.refdata.batchload.wind.quarterlyreport;

import com.flying.refdata.batchload.wind.ExcelEquityProcessor;
import com.flying.refdata.model.Quarter;
import com.flying.refdata.model.equity.EquityInfo;
import com.flying.refdata.model.equity.QuarterlyReport;
import com.flying.refdata.service.EquityService;
import com.flying.refdata.service.ReportService;
import com.flying.utils.excel.ExcelUtil;
import com.flying.utils.lang.IntUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Service
public class ExcelQuarterlyReportProcessor implements ExcelEquityProcessor<QuarterlyReport> {
    private static final int TITLE_ROW_INDEX = 0;
    private static final int MIN_DATA_ROW_INDEX = 1;
    private static final int MIN_DATA_COL_INDEX = 2;
    private static final int EQUITY_CODE_COL_INDEX = 0;
    private static final int EQUITY_NAME_COL_INDEX = 1;
    private static final String EQUITY_CODE = "证券代码";
    private static final String EQUITY_NAME = "证券简称";
    private static final String DISCLOSE_DATE = "定期报告实际披露日期";
    private static final String NET_ASSET_ROE = "净资产收益率ROE(加权)";
    private static final String REVENUE_GROWTH_RATE = "营业收入(同比增长率)";
    private static final String PARENT_NET_PROFIT_GROWTH_RATE = "归属母公司股东的净利润-扣除非经常损益(同比增长率)";
    private static final String CASH_FLOW_GROWTH_RATE = "经营活动产生的现金流量净额(同比增长率)";

    private static final String ANY = "[\\s\\S]*";
    private static final String QUARTER_FIRST = ANY + "一季" + ANY;
    private static final String QUARTER_SECOND = ANY + "中报" + ANY;
    private static final String QUARTER_THIRD = ANY + "三季" + ANY;
    private static final String QUARTER_FOURTH = ANY + "年报" + ANY;

    private static final int BATCH_SIZE = 30;

    @Autowired
    private EquityService equityService;

    @Autowired
    private ReportService reportService;

    @Override
    public boolean isValidSheet(Sheet sheet) {
        Row row = sheet.getRow(TITLE_ROW_INDEX);
        return ExcelUtil.indexOf(row, EQUITY_CODE) == EQUITY_CODE_COL_INDEX && ExcelUtil.indexOf(row, EQUITY_NAME) >= EQUITY_NAME_COL_INDEX;
    }

    @Override
    public boolean isDataRow(Sheet sheet, Row row) {
        return row.getRowNum() >= MIN_DATA_ROW_INDEX
                && !ExcelUtil.isEmpty(row.getCell(EQUITY_CODE_COL_INDEX))
                && !ExcelUtil.isEmpty(row.getCell(EQUITY_NAME_COL_INDEX));
    }

    @Override
    public void handleRow(Sheet sheet, Row row) {
        if (!isDataRow(sheet, row)) return;
        EquityInfo info = equityService.getOrSaveEquityInfo(ExcelUtil.asString(row.getCell(EQUITY_CODE_COL_INDEX)), ExcelUtil.asString(row.getCell(EQUITY_NAME_COL_INDEX)));
        Map<String, QuarterlyReport> reports = new HashMap<>();
        QuarterlyReport currentReport;
        String title;
        int year;
        Quarter quarter;
        String compositeKey;
        for (Cell cell : row) {
            if (ExcelUtil.isEmpty(cell) || cell.getColumnIndex() < MIN_DATA_COL_INDEX) continue;
            title = getTitle(sheet, cell);
            year = getCellRelatedYear(title);
            quarter = getCellRelatedQuarter(title);
            compositeKey = info.getCode() + year + quarter.toString();
            if (reports.containsKey(compositeKey)) {
                currentReport = reports.get(compositeKey);
            } else {
                currentReport = reportService.getOrCreateQuarterlyReport(info, year, quarter);
                reports.put(compositeKey, currentReport);
            }
            updateQuarterlyReport(currentReport, sheet, cell);
        }
        reportService.saveQuarterlyReport(reports.values());
    }

    private Map<String, QuarterlyReport> flushQuarterlyReport(Map<String, QuarterlyReport> reports, boolean forced) {
        if (reports.size() >= BATCH_SIZE || forced) {
            reportService.saveQuarterlyReport(reports.values());
            reports.clear();
        }
        return reports;
    }

    private String getTitle(Sheet sheet, Cell cell) {
        return ExcelUtil.asString(sheet.getRow(TITLE_ROW_INDEX).getCell(cell.getColumnIndex()));
    }

    private int getCellRelatedYear(String title) {
        Matcher m = Pattern.compile("\\d+").matcher(title);
        if (m.find()) return Integer.valueOf(m.group());
        return -1;
    }

    private Quarter getCellRelatedQuarter(String title) {
        if (Pattern.matches(QUARTER_FIRST, title)) return Quarter.FIRST;
        if (Pattern.matches(QUARTER_SECOND, title)) return Quarter.SECOND;
        if (Pattern.matches(QUARTER_THIRD, title)) return Quarter.THIRD;
        if (Pattern.matches(QUARTER_FOURTH, title)) return Quarter.FOURTH;
        throw new RuntimeException("Can not find the quarter in " + title);
    }

    private void updateQuarterlyReport(QuarterlyReport report, Sheet sheet, Cell cell) {
        String title = getTitle(sheet, cell);
        if (StringUtils.containsIgnoreCase(title, DISCLOSE_DATE)) {
            report.setDiscloseDate(IntUtil.getDate(ExcelUtil.asDate(cell).getTime()));
        } else if (StringUtils.containsIgnoreCase(title, NET_ASSET_ROE)) {
            double roe = ExcelUtil.asDouble(cell);
            switch (report.getQuarter()) {
                case FIRST:
                    roe = roe * 4;
                    break;
                case SECOND:
                    roe = roe * 2;
                    break;
                case THIRD:
                    roe = roe * 4 / 3;
                    break;
                case FOURTH:
                    break;
            }
            report.setNetAssetROE(roe);
        } else if (StringUtils.containsIgnoreCase(title, REVENUE_GROWTH_RATE)) {
            report.setRevenueGrowthRate(ExcelUtil.asDouble(cell));
        } else if (StringUtils.containsIgnoreCase(title, PARENT_NET_PROFIT_GROWTH_RATE)) {
            report.setParentNetProfitGrowthRate(ExcelUtil.asDouble(cell));
        } else if (StringUtils.containsIgnoreCase(title, CASH_FLOW_GROWTH_RATE)) {
            report.setCashFlowGrowthRate(ExcelUtil.asDouble(cell));
        }
    }
}